home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-20
/
cat990.zip
/
RS232.H
< prev
next >
Wrap
C/C++ Source or Header
|
1993-02-01
|
17KB
|
500 lines
/***
RS232: Set of general purpose functions providing fully buffered
interrupt driven serial I/0. Supports baud rates from 110 to
115.2K on serial ports 1 - 4. Compiled and tested with Turbo C
and Borland C 2.0. Note - do not compile with stack overflow
checking on!
3/5/92
C. Karcher
Seattle WA
CSID 76406,536
***/
#if !defined(__DOS_H)
#include<dos.h> /* need dos.h for port i/o and interrupt functions */
#endif
/*****************************************************************/
/*** global variables - all names begin with rs_ ***/
/*****************************************************************/
void interrupt (*rs_oldvec)(void) = NULL;
int rs_intno,rs_thr,rs_rbr,rs_iir,rs_ier;
unsigned rs_ibufsiz,rs_obufsiz;
unsigned volatile rs_inhead,rs_intail,rs_outhead,rs_outtail;
char *rs_inbuf,*rs_outbuf,rs_oldmask;
/*****************************************************************/
/*** function prototypes ***/
/*****************************************************************/
/* interrupt handler */
void interrupt rs_inthndlr(void);
/*------------------cut here for function quick reference-------------------*/
/* port initialization: Sets up port parameters, installs interrupt
vector, enables interrupts. Returns 0 on success. Input and output
buffers are 'rotary' buffers - buffer size must be a power of 2.
Example:
status = rs_initport(port,baud,parity,bits,stop,in_bufsize,in_bufptr,\
out_bufsize,out_bufptr);
port = '1','2','3', or '4'
baud = 110, 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600 or 115200
parity = 'N', 'E', 'O', 'S' or 'M'
bits = '7' or '8'
stop = '1' or '2'
in_bufsize = (power of 2) >= 2 <= 32768
in_bufptr = char pointer to previously allocated input buffer
out_bufsize = (power of 2) >= 2 <= 32768
out_bufptr = char pointer to previously allocated output buffer */
int rs_initport(char, long, char, char, char,\
unsigned, char *, unsigned, char *);
/* Send single byte out port - if no room in output buffer, wait
til there's room. */
void rs_sndbyt(char);
/* Send string of specified length out port - if no room in output
buffer, waits til there's room. */
void rs_sndstr(int,char *);
/* Get single byte, wait if none available. */
char rs_getbyt(void);
/* Get newline terminated string (not to exceed specified number of
characters). Include newline and null terminate. Wait if
characters are unavailable. Return number of characters read.*/
int rs_getlin(int, char *);
/* Get specified number of characters from input buffer. Wait if
unavailable. */
void rs_getstr(int, char *);
/* Return number of characters waiting to be read from input buffer. */
unsigned rs_inrcvd(void);
/* Return amount of free space in output buffer. */
unsigned rs_outfre(void);
/* Call this function before exiting program or when finished with port.
Restores interrupt vector and interrupt mask, and disables port
interrupts. */
void rs_close(void);
/*------------------cut here for function quick reference-------------------*/
/* Interrupt service routine - Get received character from port rs_rbr,
store in rs_inbuf and increment rs_inrcv */
void interrupt rs_inthndlr(void)
{
extern void interrupt (*rs_oldvec)(void);
extern char *rs_inbuf,*rs_outbuf;
extern unsigned rs_ibufsiz,rs_obufsiz;
extern volatile unsigned rs_intail,rs_outhead,rs_outtail;
extern int rs_rbr,rs_iir,rs_thr;
enable(); /* enable interrupts - code is reentrant */
_AL = inportb(rs_iir); /* get interrupt id */
if(!(_AL & '\x01')){ /* interrupt pending ? */
do{
if((_AL & '\x04')){ /* data received interrupt ? */
*(rs_inbuf + rs_intail++) = inportb(rs_rbr); /* get the byte */
rs_intail &= rs_ibufsiz; /* in tail pointer wrapped around */
}
else if(rs_outhead != rs_outtail){ /* send a byte if any to send */
outportb(rs_thr,*(rs_outbuf + rs_outhead++));
rs_outhead &= rs_obufsiz;
}
}while(!((_AL = inportb(rs_iir)) & '\x01')); /* loop if int. pending */
}
else
rs_oldvec(); /* no interrupt pending on entry so this ain't our interrupt */
outportb('\x20','\x20'); /* acknowledge 8259 */
}
/* rs_initport: Initialize port, interrupt vector and interrupt mask (see
description with function prototype for details). Return 0 on success
and -1 on failure. */
int rs_initport(char rs_port,long rs_baud,char rs_parity,char rs_bits,\
char rs_stop,unsigned rs_userinbufsiz, char *rs_userinbuf,\
unsigned rs_useroutbufsiz,char *rs_useroutbuf)
{
extern void interrupt (*rs_oldvec)(void);
void interrupt (*rs_intfnc)(void);
extern unsigned rs_obufsiz,rs_ibufsiz;
extern volatile unsigned rs_inhead,rs_intail,rs_outhead,rs_outtail;
extern int rs_intno,rs_thr,rs_rbr,rs_iir,rs_ier;
extern char rs_oldmask;
extern char *rs_inbuf,*rs_outbuf;
int rs_dll,rs_dlm,rs_lcr,rs_msr,rs_mcr,rs_lsr,rs_portbase;
char rs_dvsrl,rs_dvsrh,rs_mask;
if(rs_oldvec != NULL) /* if there's already a port open, forget it */
return -1;
rs_oldmask = '\0';
/* make sure buffer size is valid */
if((rs_userinbufsiz - 1) & rs_userinbufsiz)
return -1;
rs_ibufsiz = rs_userinbufsiz - 1;
if((rs_inbuf = rs_userinbuf) == NULL)
return -1;
if((rs_useroutbufsiz - 1) & rs_useroutbufsiz)
return -1;
rs_obufsiz = rs_useroutbufsiz - 1;
if((rs_outbuf = rs_useroutbuf) == NULL)
return -1;
/* initialize buffer head and tail pointers */
rs_inhead = rs_intail = rs_outhead = rs_outtail = 0;
switch(rs_port){ /* find i/o port address, interrupt number mask */
case '1':
rs_portbase = 0x3F8;
rs_intno = 0x0C;
rs_mask = '\xEF';
break;
case '2':
rs_portbase = 0x2F8;
rs_intno = 0x0B;
rs_mask = '\xF7';
break;
case '3':
rs_portbase = 0x3E8;
rs_intno = 0x0C;
rs_mask = '\xEF';
break;
case '4':
rs_portbase = 0x2E8;
rs_intno = 0x0B;
rs_mask = '\xF7';
break;
default:
return -1;
}
switch(rs_parity){
case 'N':
rs_parity = 0;
break;
case 'E':
rs_parity = '\x18';
break;
case 'O':
rs_parity = '\x08';
break;
case 'S':
rs_parity = '\x38';
break;
case 'M':
rs_parity = '\x28';
break;
default:
return -1;
}
if(rs_bits == '7')
rs_bits = 2;
else if(rs_bits == '8')
rs_bits = 3;
else
return -1;
if(rs_stop == '1')
rs_stop = 0;
else if(rs_stop == '2')
rs_stop = 4;
else
return -1;
/* 8250 (or 16x50) registers: */
/* out, bit 7 of LCR = 0, (Transmit Holding Register) */
rs_thr = rs_portbase + 0;
/* in, bit 7 of LCR = 0, (Receive Buffer Register) */
rs_rbr = rs_portbase + 0;
/* out, bit 7 of LCR = 1, (Divisor Latch LSB) */
rs_dll = rs_portbase + 0;
/* out, bit 7 of LCR = 1, (Divisor Latch MSB) */
rs_dlm = rs_portbase + 1;
/* out, bit 7 of LCR = 0, (Interrupt Enable Register)
bit 0 = 1 data rcvd
bit 1 = 1 transmit holding reg. empty - the act of
setting this bit will generate the interrupt
bit 2 = 1 data reception error
bit 3 = 1 change in modem status
bit 4-7 unused */
rs_ier = rs_portbase + 1;
/* in, (Interrupt ID register)
bit 0 : 0 = interrupt pending
bits 2-1: 00 = modem status change - read status
01 = transmit ready - output character or
read iir to clear
10 = data rcvd - read data
11 = break or error */
rs_iir = rs_portbase + 2;
/* out, (Line Control Register)
rs_bits 0-1: Character Length
00 = 5 bits
01 = 6 bits
10 = 7 bits
11 = 8 bits
bit 2: Number of stop bits
0 = 1 (1.5 if character length 5)
1 = 2
bit 3: Parity
0 = no parity
1 = parity generated
bit 4: Parity type
0 = odd
1 = even
bit 5: Stick Parity
0 = disabled
1 = always 1 if bit 3 = 1 & bit 4 = 0 or
always 0 if bit 3 = 1 & bit 4 = 1 or
no parity if bit 3 = 0
bit 6: Set Break
0 = disabled
1 = output string of 0s
bit 7: Enable write to baud divisor regs. if 1 */
rs_lcr = rs_portbase + 3;
/* out, (Modem Control Register)
bit 0: 1 = data terminal ready
bit 1: 1 = request to send
bit 2: 1 = aux. output 1
bit 3: 1 = aux. output 2
bit 4: 1 = UART loopback mode
bit 5-7: always 0 */
rs_mcr = rs_portbase + 4;
/* in, (Line Status Register)
bit 0: 1 = character received
bit 1: 1 = rcvd data overrun
bit 2: 1 = parity error
bit 3: 1 = framing error
bit 4: 1 = break detected
bit 5: 1 = transmit holding reg. empty
bit 6: 1 = transmit shift reg. empty
bit 7: 1 = time-out (off line) */
rs_lsr = rs_portbase + 5;
/* in, (Modem Status Register)
bit 0: 1 = change in clear to send
bit 1: 1 = change in data set ready
bit 2: 1 = change in ring indicator
bit 3: 1 = change in data carrier detect
bit 4: 1 = clear to send
bit 5: 1 = data set ready
bit 6: 1 = ring indicator
bit 7: 1 = data carrier detect */
rs_msr = rs_portbase + 6;
/* get the baud rate divisor values */
rs_dvsrh = 0;
switch(rs_baud){
case 110L:
rs_dvsrh = '\x04';
rs_dvsrl = '\x17';
break;
case 300L:
rs_dvsrh = '\x01';
rs_dvsrl = '\x80';
break;
case 600L:
rs_dvsrl = '\xC0';
break;
case 1200L:
rs_dvsrl = '\x60';
break;
case 2400L:
rs_dvsrl = '\x30';
break;
case 4800L:
rs_dvsrl = '\x18';
break;
case 9600L:
rs_dvsrl = '\x0C';
break;
case 19200L:
rs_dvsrl = '\x06';
break;
case 38400L:
rs_dvsrl = '\x03';
break;
case 57600L:
rs_dvsrl = '\x02';
break;
case 115200L:
rs_dvsrl = '\x01';
break;
default:
return -1;
}
rs_oldvec = getvect(rs_intno); /* get the old interrupt vector */
setvect(rs_intno,rs_inthndlr); /* plug in the new one */
outportb(rs_ier,0); /* disable UART interrupts */
outportb(rs_lcr,'\x80'); /* enable baud rate divisor registers */
outportb(rs_dll,rs_dvsrl); /* write divisor lo byte */
outportb(rs_dlm,rs_dvsrh); /* write divisor hi byte */
outportb(rs_lcr,(rs_parity | rs_bits | rs_stop)); /* write misc. parameters */
outportb(rs_mcr,'\x0B'); /* turn on RTS and DTR lines */
outportb(rs_ier,'\x03'); /* enable data rcvd hardware interrupts */
inportb(rs_iir); /* read out...*/
inportb(rs_rbr); /*...any garbage...*/
inportb(rs_lsr); /*...left in...*/
inportb(rs_msr); /*...registers */
disable();
rs_oldmask = inportb(0x21); /* save old interrupt controller mask */
rs_mask &= rs_oldmask;
outportb(0x21,rs_mask); /* interrupt now enabled */
enable();
return 0;
}
/* rs_close: Restore original 8259 interrupt controller mask value, disable
UART interrupts and restore original interrupt vector. */
void rs_close(void)
{
extern void interrupt (*rs_oldvec)(void);
extern int rs_intno,rs_ier;
extern char rs_oldmask;
if(rs_oldmask){
disable();
outportb(0x21,rs_oldmask); /* restore old interrupt mask value */
enable();
}
outportb(rs_ier,0); /* disable UART interrupts */
if(rs_oldvec != NULL)
setvect(rs_intno,rs_oldvec); /* restore old interrupt vector */
rs_oldvec = NULL;
}
/* rs_sndbyt: Output byte via output buffer. If no space in output buffer,
wait til there is. */
void rs_sndbyt(char rs_snd)
{
extern char *rs_outbuf;
extern int rs_ier;
extern volatile unsigned rs_outhead,rs_outtail;
extern unsigned rs_obufsiz;
while(((rs_outtail + 1) & rs_obufsiz) == rs_outhead)
; /* make sure there's room in the buffer */
*(rs_outbuf + rs_outtail++) = rs_snd;
rs_outtail &= rs_obufsiz;
outportb(rs_ier,'\x03'); /* generate an interrupt to send the char */
}
/* rs_sndstr: Output rs_outcnt chars from rs_str to output buffer. If not
enough space in output buffer, wait til there is.*/
void rs_sndstr(int rs_sndcnt, char *rs_str)
{
extern char *rs_outbuf;
extern volatile unsigned rs_outhead,rs_outtail;
extern unsigned rs_obufsiz;
int rs_x;
for(rs_x = 0;rs_x < rs_sndcnt;rs_x++){
while(((rs_outtail + 1) & rs_obufsiz) == rs_outhead)
; /* make sure there's room in the buffer */
*(rs_outbuf + rs_outtail++) = *(rs_str + rs_x);
rs_outtail &= rs_obufsiz;
}
outportb(rs_ier,'\x03'); /* generate an interrupt to start buffer transmit */
}
/* rs_outfre: Return amount of free space available in output buffer. */
unsigned rs_outfre(void)
{
extern volatile unsigned rs_outhead,rs_outtail;
extern unsigned rs_obufsiz;
return(rs_obufsiz + 1 - ((rs_outtail - rs_outhead) & rs_obufsiz));
}
/* rs_getbyt: Return character byte from input buffer - wait if none avail. */
char rs_getbyt(void)
{
extern char *rs_inbuf;
extern volatile unsigned rs_inhead,rs_intail;
extern unsigned rs_ibufsiz;
char rs_byt;
while(rs_inhead == rs_intail) /* wait if no char available */
;
rs_byt = *(rs_inbuf + rs_inhead++);
rs_inhead &= rs_ibufsiz;
return rs_byt;
}
/*
rs_getlin: Get a maximum of 'cnt' characters from port buffer and place in
buffer 'rs_getbuf'. Stop if newline is encountered. Include new line in
array. Returns number of characters copied to buf (excluding term '\0').
*/
int rs_getlin(int rs_getcnt,char *rs_getbuf)
{
int rs_x = 0;
char rs_c;
while(rs_x < rs_getcnt){
rs_c = rs_getbyt();
*(rs_getbuf + rs_x) = rs_c;
rs_x++;
if(rs_c == '\n' || rs_x == rs_getcnt)
break;
}
*(rs_getbuf + rs_x) = '\0';
return rs_x;
}
/* rs_getstr: Get specified number of bytes from port buffer and place in
user specified buffer. Does not null terminate string. If specified
number of input chars are not available, waits til they are. */
void rs_getstr(int rs_getcnt,char *rs_getbuf)
{
int rs_x = 0;
while(rs_x < rs_getcnt)
*(rs_getbuf + rs_x++) = rs_getbyt();
}
/* rs_inrcvd: Return number of received bytes waiting to be read from input
buffer. */
unsigned rs_inrcvd(void)
{
unsigned rs_incnt;
extern volatile unsigned rs_inhead,rs_intail;
extern unsigned rs_ibufsiz;
return((rs_intail - rs_inhead) & rs_ibufsiz);
}